﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace CodeStash.Addin.Core
{
    public abstract class Validator : NotifyPropertyChanged, INotifyDataErrorInfo
    {
        protected void ValidationFailure(string key, string description)
        {
            AddValidationFailure(key, description);
            AddValidationFailure(string.Empty, description);
        }

        protected abstract void ValidateOverride();

        public bool Validate()
        {
            Dictionary<string, ICollection<object>> OldPropertiesErrors =
                new Dictionary<string, ICollection<object>>(propertiesErrors);

            propertiesErrors.Clear();

            ValidateOverride();

            bool newHasErrors = propertiesErrors.ContainsKey(string.Empty);

            if (newHasErrors && HasErrors)
            {
                HashSet<string> processed = new HashSet<string>();

                //Check what is the same
                foreach (KeyValuePair<string, ICollection<object>> pair in OldPropertiesErrors)
                {
                    ICollection<object> newErrors;
                    if (propertiesErrors.TryGetValue(pair.Key, out newErrors))
                    {
                        if (ErrorsListChanged(newErrors, pair.Value))
                        {
                            processed.Add(pair.Key);
                            continue;
                        }
                    }

                    OnErrorsChanged(pair.Key);
                    processed.Add(pair.Key);
                }

                foreach (KeyValuePair<string, ICollection<object>> pair in propertiesErrors)
                {
                    if (!processed.Contains(pair.Key))
                    {
                        OnErrorsChanged(pair.Key);
                    }
                }
            }

            return !(HasErrors = newHasErrors);
        }

        #region INotifyDataErrorInfo Members

        private Dictionary<string, ICollection<object>> propertiesErrors = new Dictionary<string, ICollection<object>>();

        private void AddValidationFailure(string property, object error)
        {
            ICollection<object> errors;
            if (!propertiesErrors.TryGetValue(property, out errors))
            {
                errors = new List<object>();
                propertiesErrors[property] = errors;
            }

            errors.Add(error);
        }

        private bool ErrorsListChanged(ICollection<object> errors1, ICollection<object> errors2)
        {
            if (errors1.Count == errors2.Count)
            {
                IEnumerator<object> enumerator2 = errors2.GetEnumerator();
                foreach (object itemFrom1 in errors1)
                {
                    enumerator2.MoveNext();
                    object itemFrom2 = enumerator2.Current;
                    if (itemFrom1 != itemFrom2)
                    {
                        return false;
                    }
                }

                return true;
            }

            return false;
        }

        private bool _hasErrors;

        public bool HasErrors
        {
            get { return _hasErrors; }
            private set
            {
                if (_hasErrors != value)
                {
                    _hasErrors = value;
                    NotifyChanged("HasErrors");
                }
            }
        }

        private object _entityError;

        public object EntityError
        {
            get
            {
                return _entityError;
            }

            protected set
            {
                if (_entityError != null)
                {
                    _entityError = value;
                    NotifyChanged("EntityError");
                }
            }
        }

        public IEnumerable GetErrors(string propertyName)
        {
            ICollection<object> result;
            return this.propertiesErrors.TryGetValue(propertyName ?? string.Empty, out result) ? result : null;
        }

        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        private void OnErrorsChanged(string propertyName)
        {
            EventHandler<DataErrorsChangedEventArgs> handler = ErrorsChanged;
            if (handler != null)
            {
                handler(this, new DataErrorsChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}